package com.kevin.utils;


import com.kevin.model.Location;
import com.kevin.model.PointsDistance;
import lombok.Getter;
import lombok.Setter;
import org.springframework.stereotype.Component;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @Description: 算法数据仓库
 *
 * list 经纬度列表仓库
 * map 经纬度点之间距离数据仓库
 *      key: id1-id2  -->id为Location中id @see com.ecej.esmart.assign.v4.algorithm.model.Location
 *      value: Location
 *
 * @author kevin
 * @version V1.0
 * @date 2019/2/11 9:59
 **/
@Component
public class PointsRep {
    /**
     * 经纬度信息数据仓库,保存经纬度信息
     */
    private final List<Location> list = new ArrayList<>();

    /**
     * 经纬度距离数据仓库，保存经纬度之间的距离
     * key: id1-id2,id1小于id2  例：1-2
     */
    private final Map<String,PointsDistance> map = new ConcurrentHashMap<>();

    /**
     * 二维list列表
     * 用于插单入参
     */
    private final List<List<Location>> twoDimentionList = new ArrayList<>();

    /**
     * 数据需要分组个数: 用于需要将数据分组的算法
     * 1:凝聚层次算法 @see com.ecej.esmart.assign.v4.algorithm.algorithm.HierarchicalClustering
     */
    @Setter
    @Getter
    private int group;

    /**
     * 用于需要起点的算法
     * 1:多点连线不相交算法 @see com.ecej.esmart.assign.v4.algorithm.algorithm.MultipointRoute
     * 2:穷举排序算法 @see com.ecej.esmart.assign.v4.algorithm.algorithm.SortRoute
     */
    @Setter
    @Getter
    private Location begin;
    /**
     * 用于需要截止点的算法
     * 1:多点连线不相交算法 @see com.ecej.esmart.assign.v4.algorithm.algorithm.MultipointRoute
     * 2:穷举排序算法 @see com.ecej.esmart.assign.v4.algorithm.algorithm.SortRoute
     */
    @Setter
    @Getter
    private Location end;

    /**
     * 半径，需要通过半径计算数据的算法使用
     * 1: 点是否在矩形区域内的算法 @see com.ecej.esmart.assign.v4.algorithm.algorithm.InPolygon
     */
    @Setter
    @Getter
    private Double radius;

    /**
     * 现在用于加单算法的加单点存储
     */
    @Setter
    @Getter
    private Location location;

    /**
     * 保存经纬度信息到仓库
     */
    private void setPoints(List<Location> locations){
        for(Location location : locations){
            list.add(location);
        }
    }

    /**
     * 从仓库获取经纬度信息
     * @return
     */
    public List<Location> getPoints(){
        return list;
    }

    /**
     * 获取仓库个点经纬度距离信息
     * key: id1-id2
     * @return
     */
    public Map<String,PointsDistance> getAllDis(){
        return map;
    }

    /**
     * 设置二维数组
     */
    public void setTwoDimentionList(List<List<Location>> list){
        for(int i=0;i<list.size();i++){
            List<Location> locations = new ArrayList<>();
            for(int j=0;j<list.get(i).size();j++){
                locations.set(j,list.get(i).get(j));
            }

            twoDimentionList.set(i,locations);
        }
    }
    /**
     * 获取二维数组
     */
    public List<List<Location>> getTwoDimentionList(){
        return twoDimentionList;
    }

    /**
     * 根据两个经纬度生成的主键获取经纬度之间的距离
     * key: id1-id2
     * @param key
     * @return
     */
    public BigDecimal getDis(String key){
        if(map.get(key)!=null){
            return map.get(key).getDistance();
        }
        return null;
    }

    /**
     * 根据两个经纬度对象的主键ID获取经纬度之间的距离
     * @param idOne
     * @param idTwo
     * @return
     */
    public BigDecimal getDis(int idOne,int idTwo){
        String key = null;
        if(idOne < idTwo){
            key = idOne + "-" + idTwo;
        }else{
            key = idTwo + "-" +idOne;
        }

        return getDis(key);
    }

    /**
     * 保存两个经纬度之间的距离到距离数据仓库
     */
    private void setDis(Location locatOne,Location locatTwo){
        /**
         *  两个经纬度生成的距离数据仓库主键
         *  第一个主键必须小于第二个主键，防止距离数据重复计算，同时统一获取方式
         */
        String key = null;
        if(locatOne.getId() < locatTwo.getId()){
            key = locatOne.getId() + "-" + locatTwo.getId();
        }else{
            key = locatTwo.getId() + "-" + locatOne.getId();
        }

        //两个经纬度之间的距离
        BigDecimal dis = BigDecimal.valueOf(Double.valueOf(DistanceUtil.algorithm(locatOne.getLon(),locatOne.getLat(),locatTwo.getLon(),locatTwo.getLat())));

        PointsDistance pointsDistance = new PointsDistance();
        //数据仓库主键
        pointsDistance.setId(key);
        //经纬度之间距离
        pointsDistance.setDistance(dis);

        //保存到距离数据仓库
        map.put(key,pointsDistance);
    }

    /**
     *  保存list中所有的数据的距离信息到距离数据仓库
     * @param list
     */
    private void setDis(List<Location> list){
        if(list.isEmpty() || list.size()<2){
            return;
        }

        /**
         * 计算所有的经纬度之间的距离，保存到距离数据仓库
         */
        for(int i=0;i<list.size()-1;i++){
            for(int j=i+1;j<list.size();j++){
                setDis(list.get(i),list.get(j));
            }
        }
    }

    /**
     * 初始化数据仓库
     * @param locations
     */
    public void initData(List<Location> locations){
        /*
            清除历史数据，保持干净的数据仓库
         */
        clearData();
        /*
            设置经纬度信息
         */
        setPoints(locations);
        /*
            设置距离信息
         */
        setDis(list);
    }

    /**
     * 清除数据仓库信息
     */
    public void clearData(){
        /*
            清除经纬度信息
         */
        list.clear();

        /*
            清除二维数组经纬度信息
         */
        twoDimentionList.clear();

        /*
            清除经纬度距离信息
         */
        map.clear();
    }
}
